/**
 * index for Sysu engine!
 * @author HuangDell Chenwy HeLongX JiangZH
 */

#include "sysu_index.h"
#include <my_dir.h>
#include "my_sys.h"
PSI_memory_key sysu_key_memory_index;
/**
 * constructor takes the maximum key length for the keys
 * @param keylen
 * @author Chenwy
 */
Sysu_index::Sysu_index(int keylen)
{
    root = NULL;
    range_ptr=NULL;
    crashed = false;
    max_key_len = keylen;
    index_file = -1;
    node_num = 0;
    block_size = max_key_len + sizeof(long long) + sizeof(int);
}

/**
 * constructor (overload) assumes existing file
 * @author Chenwy
 */
Sysu_index::Sysu_index()
{
    root = NULL;
    range_ptr=NULL;
    crashed = false;
    max_key_len = -1;
    index_file = -1;
    block_size = -1;
    node_num = 0;
}

/**
 * destructor
 * @author Chenwy
 */
Sysu_index::~Sysu_index()
{

}

/**
 * open index specified as path(path + filename)
 * @param oath
 * @return
 * @author Chenwy
 */
int Sysu_index::open_index(char *path)
{
    DBUG_ENTER("Sysu_index::open_index");

    /*
     Open the file with read/write mode,
     create the file if not found,
     treat file as binary, and use default flags;
     */
    index_file = my_open(path, O_RDWR | O_CREAT | O_BINARY | O_SHARE, MYF(0));
    if (index_file == -1)
        DBUG_RETURN(errno);
    read_header();
    DBUG_RETURN(0);
}

/*
 get the current position of the key in the index file
 @author:JiangZhh
*/
long long Sysu_index::get_index_pos(byte *buf, int key_len){
    long long pos = -1;
    DBUG_ENTER("Sysu_index::get_index_pos");
    SDE_INDEX *ndx;
    ndx = seek_index(buf, key_len);
    if (ndx != NULL)
        pos = ndx->pos;
    DBUG_RETURN(pos);
}

/*
 get the file position of the first key in index
 @author:JiangZhh
*/
long long Sysu_index::get_first_pos() {
    long long pos = -1;
    DBUG_ENTER("Sysu_index::get_first_pos");
    if (root != NULL)
        pos = root->key_ndx->pos;
    DBUG_RETURN(pos);
}

/*
 get next key in list
 @author:JiangZhh
*/
byte *Sysu_index::get_next_key() {
    byte *key = 0;
    DBUG_ENTER("Sysu_index::get_next_key");
    if (range_ptr != NULL)
    {
        // key = (byte *)my_malloc(sysu_key_memory_index,max_key_len, MYF(MY_ZEROFILL|MY_WME));
        // memcpy(key, range_ptr->key_ndx->key, range_ptr->key_ndx->length);
        key=range_ptr->key_ndx->key;
        range_ptr = range_ptr->next;
    }
    DBUG_RETURN(key);
}

/*
 get prev key in list
 @author:JiangZhh
*/
byte *Sysu_index::get_prev_key(){
    byte *key = 0;
    DBUG_ENTER("Sysu_index::get_prev_key" );
    if (range_ptr != NULL)
    {
        key = (byte *)my_malloc(sysu_key_memory_index,max_key_len, MYF(MY_ZEROFILL | MY_WME));
        memcpy(key, range_ptr->key_ndx->key, range_ptr->key_ndx->length);
        range_ptr = range_ptr->prev;
    }
    DBUG_RETURN(key);
}

/*
 get first key in list
 @author:JiangZhh
*/
byte *Sysu_index::get_first_key() {
    byte *key = 0;
    DBUG_ENTER( "Sysu_index::get_first_key");
    // when you inert a record it don't show
//    load_index();
    if (root == NULL)
        DBUG_RETURN(key);
    range_ptr = root->next;
    if (root != NULL)
    {
        key = root->key_ndx->key;
        // key = (byte *)my_malloc(sysu_key_memory_index,max_key_len, MYF(MY_ZEROFILL | MY_WME));
        // memcpy(key, n->key_ndx->key, n->key_ndx->length);
    }
    DBUG_RETURN(key);
}

/*
 get last key in list
 @author:JiangZhh
*/
byte *Sysu_index::get_last_key() {
    SDE_NDX_NODE *n = root;
    byte *key = 0;
    DBUG_ENTER("Sysu_index::get_last_key");
    while (n->next != NULL)
        n = n->next;
    if (n != NULL) {
        key=n->key_ndx->key;
        // key = (byte *)my_malloc(sysu_key_memory_index,max_key_len, MYF(MY_ZEROFILL | MY_WME));
        // memcpy(key, n->key_ndx.key, n->key_ndx.length);
    }
    DBUG_RETURN(key);
}

/*
 just close the index
 @author:JiangZhh
*/
int Sysu_index::close_index(){
    SDE_NDX_NODE *n;
    DBUG_ENTER("Sysu_index::close_index");
    if(index_file != -1)
    {
        my_close(index_file, MYF(0));
        index_file = -1;
    }
    while (root != NULL){
        n= root;
        root = root->next;
        my_free(n->key_ndx->key);
        delete n->key_ndx;
        delete n;
    }
    DBUG_RETURN(0);
}

/**
 * read header from file
 * @author:Hlx
*/
int Sysu_index::read_header() {
    int i;
    unsigned char crash;
    int len = 0;
    unsigned long long node_num_copy = 0;
    DBUG_ENTER("Sysu_index::read_header");
    if(block_size==-1)
    {
        /*
         * Seek the start of the file,read the maximum key length value.
         */
        my_seek(index_file,0L,MY_SEEK_SET, MYF(0));
        i=my_read(index_file,(unsigned char *)&node_num_copy,sizeof(unsigned long long), MYF(0));
        memcpy(&node_num,&node_num_copy,sizeof(unsigned long long));

        i=my_read(index_file,(unsigned char *)&len,sizeof(int), MYF(0));
        memcpy(&max_key_len, &len, sizeof(int));
        /*
         * Calculate block size as maximum key length plus the size of the key plus the crashed status byte.
         */
        block_size=max_key_len+sizeof(long long)+sizeof(int);
        i=my_read(index_file,(unsigned char *)&crash,sizeof(unsigned char), MYF(0));
        memcpy(&crashed,&crash,sizeof(unsigned char));
    }
    else
    {
        i=(int) my_seek(index_file,sizeof(unsigned long long) + sizeof(int)+sizeof(unsigned char),MY_SEEK_SET, MYF(0));
    }
    i = i + 1;
    DBUG_RETURN(0);
}
/**
* write header to file.
 * @author:Hlx
*/
int Sysu_index::write_header() {
    int i=0;
    int len = 0;
    unsigned char crash;
    DBUG_ENTER("Sysu_index::write_header");
    if(block_size!=-1)
    {
        /*
         * Seek the start of the file and write the maximum key length then write the crashed status byte.
         */
        my_seek(index_file,0L,MY_SEEK_SET, MYF(0));
        i = my_write(index_file, (unsigned char*)&node_num, sizeof(unsigned long long), MYF(0));
        memcpy(&len,&max_key_len,sizeof(int));
        i = my_write(index_file, (unsigned char*)&len, sizeof(int), MYF(0));
        memcpy(&crash,&crashed,sizeof(bool));
        i= my_write(index_file,&crash,sizeof(unsigned char), MYF(0));
    }
    i = i + 1;
    DBUG_RETURN(0);
}
/**
 *
 * @param ndx
 * @return
 * write a row(SDE_INDEX struct) to the index file.
 * @author:Hlx
 */
long long Sysu_index::write_row(SDE_INDEX *ndx) {
    long long pos;
    int i;
    DBUG_ENTER("Sysu_index::write_row");
    /*
     * Seek the end of the file (always append)
     */
    pos= my_seek(index_file,0L,MY_SEEK_END,MYF(0));

    /*
     * Write the length of the key.
     */
    i= my_write(index_file,(unsigned char*)&(ndx->length),sizeof(int), MYF(0));
    /*
     * Write the file position for the key value.
     */
    i=i+ my_write(index_file,(unsigned char *)&(ndx->pos),sizeof(long long), MYF(0));

    /*
    * Write the key value.
    */
    i=i+ my_write(index_file,ndx->key,ndx->length, MYF(0));

    if(i==-1)
        pos=i;
    DBUG_RETURN(pos);
}
/**
 * @param Position
 * @return
 * read a row(SDE_INDEX struct) to the index file.
 * @author:Hlx
 */
SDE_INDEX *Sysu_index::read_row(long long Position) {
    int i;
    long long pos;
    SDE_INDEX *ndx=NULL;
    DBUG_ENTER("Sysu_index::read_row");
    /*
     * Seek the position in the file (Position).
     */
    pos= my_seek(index_file,(ulong)Position,MY_SEEK_SET, MYF(0));
    if(pos!=-1L)
    {
        ndx=new SDE_INDEX();
        ndx->key = NULL;
        ndx->length = 0;
        ndx->pos = 0;
        /*
         * Read the key value.
         */
        /*
         * Read the key value.If error,return NULL.
         */
        i= my_read(index_file,(unsigned char*)&ndx->length,sizeof(int), MYF(0));

        i= my_read(index_file,(unsigned char*)&ndx->pos,sizeof(long long), MYF(0));

        ndx->key = (unsigned char*)my_malloc(sysu_key_memory_index, ndx->length, MYF(MY_ZEROFILL | MY_WME));
        i= my_read(index_file,ndx->key,ndx->length, MYF(0));

        if(i==-1)
        {
            if (ndx->key != NULL)
            {
                my_free(ndx->key);
                ndx->key = NULL;
            }
            delete ndx;
            ndx=NULL;
        }
    }
    DBUG_RETURN(ndx);
}
/**
* read the index file from disk and store in memory
 * @author:Hlx Chenwy
*/
int Sysu_index::load_index() {
    SDE_INDEX *ndx;
    int i=0;
    DBUG_ENTER("Sysu_index:load_index");
    if(root!=NULL)
        destroy_index();
    /*
     * First,read the metadata at the front of the index.
     */
    read_header();
    long long node_num_copy = node_num;
    node_num = 0;
    while(node_num_copy > 0)
    {
        ndx=new SDE_INDEX();
        ndx->key = NULL;
        ndx->length = 0;
        ndx->pos = 0;
        i= my_read(index_file,(unsigned char*)&ndx->length,sizeof(int), MYF(0));
        i= my_read(index_file,(unsigned char*)&ndx->pos,sizeof(long long), MYF(0));
        ndx->key = (unsigned char*)my_malloc(sysu_key_memory_index, ndx->length, MYF(MY_ZEROFILL | MY_WME));
        i= my_read(index_file,ndx->key,ndx->length, MYF(0));
        insert_key(ndx,false);
        node_num_copy--;
    }
    if (root)
        range_ptr=root->next;
    i = i + 1;
    DBUG_RETURN(0);
}
/**
* get current position of index file
 * @author:Hlx
*/
long long Sysu_index::curfpos()
{
    long long pos=0;
    DBUG_ENTER("Sysu_index::curfpos");
    pos=my_seek(index_file,0L,MY_SEEK_CUR, MYF(0));
    DBUG_RETURN(pos);
}
/**
* write the index back to disk
 * @author:Hlx
*/
int Sysu_index::save_index() {
    SDE_NDX_NODE *n=root;
    int i = 0;
    DBUG_ENTER("Sysu_index::save_index");
    if(index_file==-1)  // 如果已经提前关闭
        DBUG_RETURN(1);
    i= my_chsize(index_file, 0, 0, MYF(MY_WME));
    write_header();
    while(n!=NULL)
    {
        write_row(n->key_ndx);
        n=n->next;
    }
    i = i + 1;
    DBUG_RETURN(0);
}
/**
 * delete index from memory
 * @author HuangDell
*/
int Sysu_index::destroy_index() {
    SDE_NDX_NODE *n=root;
    DBUG_ENTER("Sysu_index::destroy_index");
    while(root!=NULL)
    {
        n=root;
        root=n->next;
        my_free(n->key_ndx->key);
        delete n->key_ndx;
        delete n;
    }
    root=NULL;
    DBUG_RETURN(0);
}
/**
* truncate the index file
 * @author:Hlx
*/
int Sysu_index::trunc_index() {
    DBUG_ENTER("Sysu_index::trunc_index");
    if(index_file!=-1)
    {
        my_chsize(index_file,0,0, MYF(MY_WME));
        write_header();
    }
    DBUG_RETURN(0);
}
/**
 *
 * @param key
 * @param key_len
 * @return
 * find a key in the index
 * @author:Hlx
 */
SDE_INDEX *Sysu_index::seek_index(unsigned char *key, int key_len) {
    SDE_INDEX *ndx=NULL;
    SDE_NDX_NODE *n=root;
    int buf_len;
    bool done=false;
    DBUG_ENTER("Sysu_index::seek_index");
    if(n!=NULL)
    {
        while((n!=NULL)&&!done)
        {
            buf_len=n->key_ndx->length;
            if(memcmp(n->key_ndx->key,key,(buf_len>key_len)?buf_len:key_len)==0)
                done=true;
            else
                n=n->next;
        }
    }
    if(n!=NULL){
        ndx=n->key_ndx;
        range_ptr=n->next;
    }
    DBUG_RETURN(ndx);
}
/**
 *
 * @param key
 * @param key_len
 * @return
 * find a key in the index and return position too
 * @author:Hlx
 */
SDE_NDX_NODE *Sysu_index::seek_index_pos(unsigned char *key, int key_len) {
    SDE_NDX_NODE *n=root;
    int buf_len;
    bool done=false;
    DBUG_ENTER("Sysu_index::seek_index_pos");
    if(n!=NULL)
    {
        while((n->next!=NULL)&&!done)
        {
            buf_len=n->key_ndx->length;
            if(memcmp(n->key_ndx->key,key,(buf_len>key_len)?buf_len:key_len)==0)
                done=true;
            else if(n->next!=NULL)
                n=n->next;
        }
    }
    DBUG_RETURN(n);
}

/**
 * create the index file
 * @author HuangDell
 */
int Sysu_index::create_index(char *path, int keylen) {
    DBUG_ENTER("Spartan_index::create_index");
    open_index(path);
    max_key_len = keylen;
    /* Block size is the key length plus the size of the index length variable. */
    block_size = max_key_len + sizeof(long long);
    write_header();

    DBUG_RETURN(0);

}

/**
 * insert a key into the index in memory!!!
 * Attention! IN MEMORY! 
 * @author HuangDell
 */
int Sysu_index::insert_key(SDE_INDEX *ndx, bool allow_dupes) {
    SDE_NDX_NODE *p = NULL;
    SDE_NDX_NODE *n = NULL;
    SDE_NDX_NODE *o = NULL;
    int i = -1;
    int icmp;
    bool dupe = false;
    bool done = false;
    DBUG_ENTER("Sysu_index::insert_key");
    /*
        If this is a new index，insert first key as the root node.
    */
    if (root == NULL) {
        root = new SDE_NDX_NODE();
        root->next = NULL;
        root->prev = NULL;
        root->key_ndx=ndx;
        /**
//        memcpy(root->key_ndx.key, ndx->key, ndx->length);
         */
        // root->key_ndx.key = ndx->key;
        // root->key_ndx.pos = ndx->pos;

        // root->key_ndx.length = ndx->length;
        node_num++;
    } else //set pointer to root
        p = root;
    /*
        Loop through the linked list until a value greater than the
        key to be inserted，then insert new key before that one.
    */
    while ((p != NULL) && !done) {
        icmp = memcmp(ndx->key, p->key_ndx->key,
                      (ndx->length > p->key_ndx->length) ? ndx->length : p->key_ndx->length);
        if (icmp > 0)// key is greater than current key in list
        {
            n = p;
            p = p->next;
        }
            /* If dupes not allowed,stop and return NULL*/
        else if (!allow_dupes && (icmp == 0)) {
            p = NULL;
            dupe = true; // same key occure
        } else {
            n = p->prev;  //stop，insert atn->prev
            done = true;
        }
    }
    /*
        If position found (n != NULL) and dupes permitted,
        insert key. If p is NULL insert at end else insert in middle of list.
    */
    if ((n != NULL) && !dupe) {
        if (p == NULL) //insert at end
        {
            p = new SDE_NDX_NODE();
            n->next = p;
            p->prev = n;
            /**
//            memcpy(p->key_ndx.key, ndx->key, max_key_len);
             */
            p->key_ndx=ndx;
            // p->key_ndx.key = ndx->key;
            // p->key_ndx.pos = ndx->pos;
            // p->key_ndx.length = ndx->length;
            node_num++;
        } else {
            o = new SDE_NDX_NODE();
            /**
            memcpy(o->key_ndx.key, ndx->key, max_key_len);
            */
           o->key_ndx=ndx;
            // o->key_ndx.key = ndx->key;
            // o->key_ndx.pos = ndx->pos;
            // o->key_ndx.length = ndx->length;
            o->next = p;
            o->prev = n;
            n->next = o;
            p->prev = o;
            node_num++;
        }
        i = 1;
    }

    DBUG_RETURN(i);
}

/**
 * update key in place
 * @param buf
 * @param pos
 * @param key_len
 * @return
 */
int Sysu_index::update_key(unsigned char *buf, long long pos, int key_len) {
    SDE_NDX_NODE *p;
    bool done = false;
    DBUG_ENTER("Sysu_index::update_key" );
    p= root;
    /* Search for the key.*/
    while ((p != NULL) &&!done){
        if (p->key_ndx->pos == pos)done = true;
        else
            p = p->next;
    }
    /* if key found，overwrite key value in node.*/
    if (p != NULL){
        memcpy(p->key_ndx->key,buf,key_len);}
    DBUG_RETURN(0);
}

/**
 * delete a key from the index in memory!
 * @param buf
 * @param pos
 * @param key_len
 * @return 0
 * @author HuangDell
 */
int Sysu_index::delete_key(unsigned char *buf, long long pos, int key_len) {
    SDE_NDX_NODE *p;
    int icmp;
    int buf_len;
    bool done = false;
    DBUG_ENTER("Spartan_index : :delete_key");
    p = root;
    /* Search for the key in the list. If found，delete it! */
    while ((p != NULL) && !done) {
        buf_len = p->key_ndx->length;
        icmp = memcmp(buf, p->key_ndx->key,
                      (buf_len > key_len) ? buf_len : key_len);
        if (icmp == 0) {
            if (pos != -1) {
                if (pos == p->key_ndx->pos)
                    done = true;
            } else
                done = true;
        } else
            p = p->next;
    }
    if (p != NULL) {
        /* Reset pointers for deleted node in list. */
        if (p->next != NULL)
            p->next->prev = p->prev;
        if (p->prev != NULL)
            p->prev->next = p->next;
        else
            root = p->next;
        delete p;
        node_num--;
    }
    DBUG_RETURN(0);
}
